/*
 * Amadeus Confidential Information:
 * Unauthorized use and disclosure strictly forbidden.
 * @1998-2007 - Amadeus s.a.s - All Rights Reserved.
 */
package com.amadeus.spin.devtool.soap.shooter.process;

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.devtool.soap.shooter.ProcessInput;
import com.amadeus.spin.standard.util.Dom4jUtils;
import com.amadeus.spin.standard.util.XPathHelper;

public class ReplaceInputVersion implements ProcessInput {

  /**
   * The "schema" attribute name for the root node of a request.
   */
  private static final String FULL_SCHEMA_KEY = "xsi:noNamespaceSchemaLocation";

  /**
   * The "schema" attribute name for the root node of a request.
   */
  private static final String SCHEMA_KEY = "noNamespaceSchemaLocation";

  /**
   * The "version" attribute name for the root node of a request.
   */
  private static final String VERSION_KEY = "version";

  /**
   * The pattern for a version definition is the following:
   * [mainVersion].[subVersion](.[subSubVersion])?, with... - mainVersion and
   * subVersion being from 0 to 99 (defined with one or two figures); -
   * subSubVersion from 0 to 9 (only one figure then).
   */
  public static final String VERSION_PATTERN = "[0-9]{1,2}\\.[0-9]{1,2}(\\.[0-9])?";

  private static final String VERSION_SHORT = "V";

  private static Logger log = Logger.getLogger(ReplaceInputVersion.class);

  /**
   * {@inheritDoc}
   */
  @Override
  public String processInput(Stack<String> previousOutput, String input, Node config) throws FatalException {
    String newSchemaLocationRoot = XPathHelper.getProperty(config, "@schemalocationroot");
    String newVersion = XPathHelper.getProperty(config, "@version");
    String useVersionFolder = XPathHelper.getProperty(config, "@useversionfolder");
    Document document = null;
    Element rootElement = null;
    if (newVersion == null && newSchemaLocationRoot == null) {
      // no need to go further
      return input;
    }
    document = buildInput(input);
    rootElement = document.getDocumentElement();

    if (newVersion != null) {
      Node nodeVersion = rootElement.getAttributeNode(VERSION_KEY);
      String oldVersion = StringUtils.trimToEmpty(nodeVersion.getNodeValue());
      // trim away V or v from the version
      if (newVersion.toUpperCase().startsWith(VERSION_SHORT)) {
        newVersion = newVersion.substring(VERSION_SHORT.length());
      }
      String newAttributeVersion = getNewVersion(newVersion, useVersionFolder);
      nodeVersion.setNodeValue(newAttributeVersion);
      log.info("setting schema input version to " + newAttributeVersion + " (old value : " + oldVersion + ")");
    }

    if (newSchemaLocationRoot != null) {
      Attr oldSchema = rootElement.getAttributeNode(SCHEMA_KEY);
      if (oldSchema == null) {
        oldSchema = rootElement.getAttributeNode(FULL_SCHEMA_KEY);
      }
      if (oldSchema != null) {
        String oldSchemaLoc = StringUtils.trimToEmpty(oldSchema.getNodeValue());
        String newSchema = replaceVersionInSchema(oldSchemaLoc, newSchemaLocationRoot, newVersion, useVersionFolder);
        oldSchema.setNodeValue(newSchema);
        log.info("setting schema input location to " + newSchema + " (old value : " + oldSchemaLoc + ")");
      }
    }
    return Dom4jUtils.getStringFromDocument(document);
  }

  /**
   * computes the new version attribute of the xml input
   *
   * @param newVersion
   * @param useVersionFolder
   * @return
   */
  private String getNewVersion(String newVersion, String useVersionFolder) {
    String newAttributeVersion = "";
    if (useVersionFolder(useVersionFolder)) {
      newAttributeVersion = VERSION_SHORT + newVersion;
    }
    if (useVersionFolder(useVersionFolder)) {
      newAttributeVersion = VERSION_SHORT + newVersion;
    }
    return newAttributeVersion;
  }

  /**
   * A utility to replace stuff compliant to the version pattern with the new
   * version value given.
   *
   * @param oldSchema
   *          the String with potentially old versions that have to be replaced;
   * @param newPattern
   *          the new version to put in our String;
   * @return the string modified given the new versions.
   * @throws UnsupportedOperationException
   *           (in case of old jdk, or bad version input given)
   */
  public static String replaceVersionInSchema(String oldSchema, String newSchema, String newVersion,
      String useVersionFolder) {
    Matcher versionMatcher = Pattern.compile(VERSION_PATTERN).matcher(newVersion);
    if (!versionMatcher.matches()) {
      throw new UnsupportedOperationException("The version '" + newVersion + "' given in paramater is not valid");
    }
    String fullVersionString = VERSION_SHORT + newVersion + "/";
    String result = oldSchema.replaceAll(VERSION_SHORT + VERSION_PATTERN + "/", fullVersionString);
    if (newSchema != null) {
      int schemaEndIndex = result.indexOf(fullVersionString);
      // if the defined location root does not include a slash in the end of the
      // string, then use the one defined in the input file
      if (newSchema.charAt(newSchema.length() - 1) != '/') {
        schemaEndIndex--;
      }
      if (!useVersionFolder(useVersionFolder)) {
        schemaEndIndex += fullVersionString.length();
      }
      result = newSchema + result.substring(schemaEndIndex);
    }
    return result;
  }

  private static boolean useVersionFolder(String param) {
    return !"false".equalsIgnoreCase(param) && !"no".equalsIgnoreCase(param);
  }

  private static Document buildInput(String input) throws FatalException {
    try {
      return Dom4jUtils.getDocumentFromString(input);
    }
    catch (FatalException e) {
      log.error("cannot build DOM from input");
      throw new FatalException("cannot build DOM from input." + e.getMessage(), e);
    }
  }

}
